package com.smart.auth.jwt;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.smart.auth.config.JWTConfigProperties;
import com.smart.auth.dto.MemberDto;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.Data;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;

/**
 * 创建token
 * 从token中获取数据
 * 刷新token
 */
@Component
@Data
public class JwtTokenUtils {
    /**
     * 自定义信息中用户角色
     */
    private static final String ROLE_CLAIMS = "roles";
    /**
     * 自定义信息中用户ID
     */
    private static final String UID_CLAIMS = "uid";
    /**
     * 自定义信息中用户名
     */
    private static final String USERNAME_CLAIMS = "username";
    /**
     * 配置信息
     */
    @Resource
    JWTConfigProperties jwtConfigProperties;


    /**
     * Hash加密
     * 对称加密  AES  DES  提供秘钥
     * 非得对称加密 RSA256   RSA512     公钥  私钥
     * 不可以加密   MD5
     */


    /**
     * 创建token
     * 7天
     * 后台管理
     *
     * @param
     * @return
     */
    public String createToken(MemberDto memberDto) throws JsonProcessingException {
        // 如果选择记住我，则token的过期时间为
        Map<String, Object> claims = new HashMap<>();
        claims.put(UID_CLAIMS, memberDto.getMemberId());
        claims.put(USERNAME_CLAIMS, memberDto.getUsername());
        return
                // 1. 加密算法
                Jwts.builder().signWith(SignatureAlgorithm.HS256, jwtConfigProperties.getSecret())
                        // 自定义信息
                        .setClaims(claims)
                        // jwt发行人
                        .setIssuer(jwtConfigProperties.getIssuer())
                        // jwt面向的用户
                        .setSubject(memberDto.getUsername())
                        // jwt发行时间
                        .setIssuedAt(new Date())
                        // key过期时间
                        .setExpiration(new Date(System.currentTimeMillis() + jwtConfigProperties.getExpiration()))
                        .compact();
    }

    /**
     * 获取用户信息
     *
     * @param token
     * @return
     */
    public MemberDto getUserDetail(String token) throws JsonProcessingException {
        Claims claims = getTokenBody(token);
        MemberDto user = null;
        if (claims != null && claims.size() > 0) {
            user = new MemberDto();
            if (claims.get(UID_CLAIMS) != null) {
                user.setMemberId((Long) claims.get(UID_CLAIMS));
            }
            if (claims.get(JwtTokenUtils.USERNAME_CLAIMS) != null) {
                user.setUsername((String) (claims.get(USERNAME_CLAIMS)));
            }
        }
        return user;
    }

    /**
     * 获取Token有效期ms
     *
     * @param token
     * @return
     */
    public long getExpireTime(String token) {
        Claims claims = getTokenBody(token);
        return claims.getExpiration().getTime() - claims.getIssuedAt().getTime();
    }

    /**
     * 从token获取用户信息
     *
     * @param token
     * @return
     */
    public String getUsername(String token) {
        return getTokenBody(token).getSubject();
    }

    /**
     * 从token中获取用户角色
     *
     * @param token
     * @return
     */
//    public Set<String> getUserRole(String token) throws JsonProcessingException {
//        String json = (String) getTokenBody(token).get(ROLE_CLAIMS);
//        List<RoleVo> userAuthorities =
//                objectMapper.readValue(json
//                        , new TypeReference<List<RoleVo>>() {
//                        });
//        return AuthorityUtils.authorityListToSet(userAuthorities);
//    }

    /**
     * 从token中获取用户ID
     *
     * @param token
     * @return
     */
    public String getUserID(String token) {
        return (String) getTokenBody(token).get(UID_CLAIMS);
    }

    /**
     * 是否已过期
     *
     * @param token
     * @return
     */
    public boolean isExpiration(String token) {
        return getTokenBody(token).getExpiration().before(new Date());
    }

    /**
     * 刷新token
     *
     * @param token  原token
     * @param always 强制刷新，true有效
     * @return
     */
    public String refreshToken(String token, boolean always) {
        boolean canRefresh = canRefresh(token);
        String news = null;
        if (canRefresh || always) {
            Claims claims = getTokenBody(token);
            HashMap<String, Object> map = new HashMap<>();
            map.put(ROLE_CLAIMS, claims.get(ROLE_CLAIMS));
            map.put(UID_CLAIMS, claims.get(UID_CLAIMS));
            map.put(USERNAME_CLAIMS, claims.get(USERNAME_CLAIMS));
            // 加密算法
            news = Jwts.builder().signWith(SignatureAlgorithm.HS256, jwtConfigProperties.getSecret())
                    // 自定义信息
                    .setClaims(map)
                    // jwt发行人
                    .setIssuer(jwtConfigProperties.getIssuer())
                    // jwt面向的用户
                    .setSubject((String) claims.get(USERNAME_CLAIMS))
                    // jwt发行时间
                    .setIssuedAt(new Date())
                    // key过期时间
                    .setExpiration(new Date(System.currentTimeMillis()
                            + (claims.getExpiration().getTime() - claims.getIssuedAt().getTime())))
                    .compact();
        } else {
            news = token;
        }
        return news;
    }

    /**
     * 是否可刷新
     *
     * @param token
     * @return
     */
    public boolean canRefresh(String token) {
        Claims claims = getTokenBody(token);
        long create = claims.getIssuedAt().getTime();
        long expire = claims.getExpiration().getTime();
        // 有效期不足一半，可刷新
        return System.currentTimeMillis() - create > (expire - create) / 2;
    }

    /**
     * token 过期
     * token  伪造
     *
     * @param token
     * @return
     */
    private Claims getTokenBody(String token) {
        // 认证失败
        // token 过期
        return Jwts.parser().setSigningKey(jwtConfigProperties.getSecret())
                .parseClaimsJws(token).getBody();
    }

    /**
     * 验证token
     *
     * @param token
     * @return
     */
    public boolean validateToken(String token) {
        return (!isExpiration(token));
    }

}
